package io.github.openfeign.spring;

import io.github.openfeign.spring.enhancer.ApacheHttpClientBytecodeTransformer;
import io.github.openfeign.spring.enhancer.ConsumerHttpEntityBytecodeTransformer;
import io.github.openfeign.spring.enhancer.DefaultClientBytecodeTransformer;
import io.github.openfeign.spring.enhancer.MultipartFormContentProcessorBytecodeTransformer;
import io.github.openfeign.spring.enhancer.OutputBytecodeTransformer;
import io.github.openfeign.spring.enhancer.RequestTemplateBytecodeTransformer;
import io.github.openfeign.spring.enhancer.SpringSingleMultipartFileWriterBytecodeTransformer;
import javassist.ClassPool;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.lang.NonNull;

import java.util.stream.Stream;


/**
 * Feign 大文件流式传输初始化器
 *
 * <h3>核心功能：</h3>
 * <ul>
 *   <li>通过字节码增强实现Feign客户端的流式文件传输能力</li>
 *   <li>解决传统multipart/form-data请求中的内存瓶颈问题</li>
 *   <li>支持GB级大文件上传场景</li>
 * </ul>
 *
 * <h3>技术实现：</h3>
 * <p>在Spring应用上下文初始化阶段，动态修改以下关键类的字节码：</p>
 * <table class="enhanced-classes">
 *   <tr><th>转换器类</th><th>功能描述</th></tr>
 *   <tr><td>RequestTemplateBytecodeTransformer</td><td>请求模板增强，增加延迟输出控制</td></tr>
 *   <tr><td>OutputBytecodeTransformer</td><td>输出流改造，支持分块写入</td></tr>
 *   <tr><td>SpringSingleMultipartFileWriterBytecodeTransformer</td><td>文件写入逻辑重写，实现流式处理</td></tr>
 *   <tr><td>MultipartFormContentProcessorBytecodeTransformer</td><td>表单处理器增强，支持消费者模式</td></tr>
 *   <tr><td>ConsumerHttpEntityBytecodeTransformer</td><td>HTTP实体改造，支持延迟内容生成</td></tr>
 *   <tr><td>DefaultClientBytecodeTransformer</td><td>默认客户端适配，保持协议兼容</td></tr>
 *   <tr><td>ApacheHttpClientBytecodeTransformer</td><td>Apache客户端适配，完善生态支持</td></tr>
 * </table>
 *
 * <h3>环境配置：</h3>
 * <pre class="config-example">
 * # application.properties
 * feign.enhancer.stream=true  # 启用流式增强
 * feign.enhancer.out=/tmp/feign-bytecode  # 输出修改后的字节码（调试用）
 * feign.enhancer.buff-size=4096  # 缓冲区大小
 * </pre>
 *
 * <h3>兼容性说明：</h3>
 * <div class="compatibility">
 *   <p>已验证版本组合：</p>
 *   <ul>
 *     <li>feign-form ≥ 3.8.0</li>
 *     <li>feign-core ≥ 11.8</li>
 *     <li>Spring Boot ≥ 2.5.x</li>
 *   </ul>
 *   <p class="warning">⚠️ 启用前请确保依赖版本兼容</p>
 * </div>
 */
@Slf4j
public class FeignMultipartBytecodeEnhancerInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    /**
     * 功能开关配置项
     */
    private static final String STREAM_ENABLED_KEY = "feign.enhancer.stream";

    /**
     * 字节码输出路径配置项
     */
    private static final String BYTECODE_OUTPUT_KEY = "feign.enhancer.out";

    /**
     * 缓冲大小配置
     */
    private static final String BYTECODE_BUFF_KEY = "feign.enhancer.buff-size";
    /**
     * 缓冲区大小.
     */
    private static final long DEFAULT_BUFFER_SIZE = 4096L;

    /**
     * Spring上下文初始化入口
     *
     * <p>执行流程：</p>
     * <ol>
     *   <li>检查流式传输功能开关</li>
     *   <li>初始化Javassist类操作池</li>
     *   <li>加载并执行所有字节码增强器</li>
     *   <li>记录增强处理结果</li>
     * </ol>
     *
     * @param applicationContext 可配置的Spring应用上下文
     */
    @Override
    public void initialize(@NonNull ConfigurableApplicationContext applicationContext) {
        // 获取环境变量配置
        final ConfigurableEnvironment env = applicationContext.getEnvironment();

        if (!isStreamingEnabled(env)) {
            if (log.isInfoEnabled()) {
                log.info("Feign streaming feature is not enabled");
            }
            return;
        }
        // 获取字节码输出路径（用于调试时检查修改后的字节码）
        final String outPath = env.getProperty(BYTECODE_OUTPUT_KEY);
        if (log.isInfoEnabled()) {
            log.info("Enabling Feign streaming, bytecode output directory: {}", outPath);
        }

        final long buffSize = env.getProperty(BYTECODE_BUFF_KEY, Long.class, DEFAULT_BUFFER_SIZE);

        executeBytecodeTransformations(new ClassPool(true), outPath, buffSize);
    }

    /**
     * 执行字节码转换流程
     *
     * @param classPool Javassist类池实例
     * @param outPath   修改后的字节码输出路径
     */
    private void executeBytecodeTransformations(final ClassPool classPool, final String outPath, final long bufferSize) {
        Stream.of(
            new RequestTemplateBytecodeTransformer(classPool, outPath),          // 增强请求模板处理
            new OutputBytecodeTransformer(classPool, outPath),                   // 修改输出流处理逻辑
            new SpringSingleMultipartFileWriterBytecodeTransformer(classPool, outPath, bufferSize), // 单文件写入处理
            new MultipartFormContentProcessorBytecodeTransformer(classPool, outPath),  // 多部分表单处理器
            new ConsumerHttpEntityBytecodeTransformer(classPool, outPath),       // HTTP 实体消费逻辑
            new DefaultClientBytecodeTransformer(classPool, outPath, bufferSize),            // 默认客户端增强
            new ApacheHttpClientBytecodeTransformer(classPool, outPath)          // Apache 客户端适配
        ).forEach(enhancer -> {
            if (log.isDebugEnabled()) {
                log.debug("Enhancing component: {}", enhancer.getClass());
            }
            enhancer.process();
        });
        if (log.isInfoEnabled()) {
            log.info("Completed streaming enhancement Feign components");
        }
    }

    /**
     * 检查流式传输功能是否启用
     *
     * @param env 环境配置
     * @return 是否启用增强功能
     */
    private boolean isStreamingEnabled(ConfigurableEnvironment env) {
        return BooleanUtils.toBoolean(env.getProperty(STREAM_ENABLED_KEY, "false"));
    }
}
